package com.huihe.framework.service;

import com.huihe.common.constant.RedisConstants;
import com.huihe.common.exception.user.UserPasswordNotMatchException;
import com.huihe.common.exception.user.UserPasswordRetryLimitExceedException;
import com.huihe.common.utils.RedisUtils;
import com.huihe.common.utils.SecurityUtils;
import com.huihe.framework.context.AuthenticationContextHolder;
import com.huihe.storage.entity.SysUser;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * 密码处理 Service 层
 */
@Component
@RequiredArgsConstructor
public class PasswordService {

    /**
     * 允许错误次数
     */
    @Value(value = "${user.password.maxRetryCount}")
    private int maxRetryCount;

    /**
     * 锁定时间
     */
    @Value(value = "${user.password.lockTime}")
    private int lockTime;

    /**
     * Redis 工具类
     */
    private final RedisUtils redisUtils;

    /**
     * 验证当前用户是否因输入错误次数过多导致锁定
     * @param sysUser 用户信息
     */
    public void validate(SysUser sysUser) {
        //从 ThreadLocal 中获得登录用的密码
        Authentication usernamePasswordAuthenticationToken = AuthenticationContextHolder.getContext();
        String username = usernamePasswordAuthenticationToken.getName();
        String password = usernamePasswordAuthenticationToken.getCredentials().toString();
        //获得当前用户错误次数
        int retryCount;
        if (!redisUtils.hasKey(getCacheKey(username))) {
            retryCount = 0;
        }else {
            retryCount = redisUtils.getCacheObject(getCacheKey(username));
        }
        //已经被锁定的情况
        if (retryCount >= maxRetryCount) {
            throw new UserPasswordRetryLimitExceedException(maxRetryCount, lockTime);
        }
        //没锁定进行比对
        if (!matches(sysUser, password)) {
            retryCount = retryCount + 1;
            //把新的失败次数存入Redis中,并设置过期时间
            redisUtils.setCacheObject(getCacheKey(username), retryCount, lockTime, TimeUnit.MINUTES);
            throw new UserPasswordNotMatchException();
        }
        else {
            clearLoginRecordCache(username);
        }
    }

    /**
     * 生成登录账户 密码错误次数 Redis 键名
     * @param username 用户名
     * @return 缓存键key: pwd_err_cnt:用户名
     */
    private String getCacheKey(String username) {
        return RedisConstants.PWD_ERR_CNT_KEY + username;
    }

    /**
     * 对前端传来的密码和数据库搜索出来的密码进行比对
     * @param sysUser 数据库搜索的密码
     * @param rawPassword 前端输入的密码
     * @return 密码是否一样
     */
    public boolean matches(SysUser sysUser, String rawPassword) {
        return SecurityUtils.matchesPassword(rawPassword, sysUser.getPassword());
    }

    /**
     * 删除Redis中的错误数
     * @param key 键
     */
    public void clearLoginRecordCache(String key) {
        if (redisUtils.hasKey(getCacheKey(key))) {
            redisUtils.deleteObject(getCacheKey(key));
        }
    }
}
